package top.malaoshi.jdbc;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.Date;

public class ReflectionUtil {


    /**
     * 将传入方法的数据类型，转换成该方法的形参类型
     * @param valueOfDb 从数据库查询出来的数据，该数据可能是String、基本数据类型的包装类型（不会是基本数据类型）、其他类型
     * @return
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    public static Object converte(Method setMethod, Object valueOfDb)
            throws NoSuchMethodException,
            InvocationTargetException,
            InstantiationException,
            IllegalAccessException {

        // set方法一定有一个参数
        Class methodParamType = setMethod.getParameterTypes()[0];
        Object ret=null;

        if(TypeUtil.isNumberWrapper(methodParamType)){// 如果方法的形参是基本类型的包装类型
            if(valueOfDb == null){
                ret = valueOfDb;
            }else if(methodParamType.isInstance(valueOfDb)){ // 如果传入的数据类型，与方法形参类型一致
                ret = valueOfDb;
            }else{ // 如果传入的数据类型，与方法形参类型 不一致
                if (TypeUtil.isNumberWrapper(valueOfDb.getClass())) { // 实参是包装类型
                    Constructor constructor = methodParamType.getConstructor(String.class);
                    Number valueNumber = (Number) valueOfDb;
                    ret = constructor.newInstance(valueNumber.toString());
                } else if(valueOfDb instanceof String){ // 实参是字符串
                    Constructor constructor = methodParamType.getConstructor(String.class);
                    String valueTemp = (String) valueOfDb;
                    ret = constructor.newInstance(valueTemp);
                }else { // 不是包装类型
                    throw new ReflectionException(setMethod.getName() + "() 方法的形参类型是：" + methodParamType.getSimpleName() +
                            "，从数据库查询出来的类型是" + valueOfDb.getClass().getSimpleName() +
                            "，类型不匹配");
                }
            }

        }else if(methodParamType.isPrimitive()){// 如果方法的形参是基本类型


            if(TypeUtil.primitiveMatchWrapper( methodParamType,valueOfDb.getClass())){ // 如果实参类型，是形参的 包装类型

                ret = valueOfDb;
            }else if(TypeUtil.isNumberWrapper(valueOfDb.getClass())){ // 实参是包装类型，形参是基本数据类型，但不一致
                // 调用转换方法，如：longValue()、shortValue()
                // 根据方法形参的类型的名字，得到方法的前缀，后面拼装"Value"
                String methodName = methodParamType.getName()+"Value";
                Method method = valueOfDb.getClass().getMethod(methodName);
                Object retTemp = method.invoke(valueOfDb);
                ret = retTemp;
            }else if ( valueOfDb instanceof BigDecimal){
                // mysql数据库的 decimal 类型，jdbc查询出来的类型是 BigDecimal，需要做额外处理
                BigDecimal temp=(BigDecimal)valueOfDb;
                if (methodParamType == double.class){
                    ret = temp.doubleValue();
                }else if(methodParamType == float.class){
                    ret = temp.floatValue();
                }else {
                    throw new ReflectionException(setMethod.getName() + "() 方法的形参类型是：" + methodParamType.getSimpleName() +
                            "，从数据库查询出来的类型是" + valueOfDb.getClass().getSimpleName() +
                            "，类型不匹配");
                }
            } else{
                throw new ReflectionException(setMethod.getName()+"() 方法的形参类型是："+methodParamType.getSimpleName()+
                        "，从数据库查询出来的类型是"+valueOfDb.getClass().getSimpleName()+
                        "，类型不匹配");
            }
        }else if(methodParamType == String.class){// 方法形参是 String
            if(valueOfDb == null){
                ret = valueOfDb;
            }else if(valueOfDb instanceof String){
                ret = valueOfDb;
            }else if(valueOfDb instanceof Number || valueOfDb instanceof Date){
                ret = valueOfDb.toString();

            }else{
                throw new ReflectionException(setMethod.getName()+"() 方法的形参类型是："+methodParamType.getSimpleName()+
                        "，从数据库查询出来的类型是"+valueOfDb.getClass().getSimpleName()+
                        "，类型不匹配");
            }

        }else{ // 其他类型，直接赋值，由上层程序处理
            ret = valueOfDb;
//            throw new ReflectionException(setMethod.getName()+"() 方法的形参类型是："+methodParamType.getSimpleName()+
//                    "，从数据库查询出来的类型是"+valueOfDb.getClass().getSimpleName()+
//                    "，类型不匹配");
        }
        return ret;
    }

}
